
package carmadness.model;

import carmadness.model.helpers.*;
import carmadness.view.Renderer;
import carmadness.Game;

import java.util.Iterator;

/**
 * @author Szelei
 * @version 1.0
 * @created 10-Apr-2010 2:32:39 PM
 */
public class Facade
{
	private BijectiveHashMap<String, GameObject> _objects = new BijectiveHashMap<String, GameObject>();

	private Facade()
	{
	}

	private static Facade instance;

	/**
	 * Visszatér az egyetlen Facade példánnyal.
	 */
	public static Facade getInstance()
	{
		if ( instance == null )
		{
			instance = new Facade();
		}
		return instance;
	}

	public void reset()
	{
		_objects.clear();
		World.getInstance().reset();
	}

	/**
	 * Visszatér egy objektum tároláshoz használt kulcsával.
	 * 
	 * @param obj
	 *            A keresendő objektum.
	 * @return Objektum kulcsa.
	 */
	public String getObjectKey( Object obj )
	{
		return _objects.reverseGet( (GameObject)obj );
	}

	public GameObject getObject( String key )
	{
		return _objects.get( key );
	}

	/**
	 * Objektumok mentése a hashmapbe.
	 * 
	 * @param key
	 *            Kulcs. (opcionális)
	 * @param obj
	 *            Érték.
	 */
	private void storeObject( String key, Object obj )
	{
		_objects.put( key == null ? "#" + String.valueOf( obj.hashCode() ) : key, (GameObject)obj );
	}

	/*
	 * Konstrukció
	 */

	/**
	 * RoadSegment (útelem) létrehozása.
	 */
	public void create_rs( String key )
	{
		RoadSegment obj = new RoadSegment();

		storeObject( key, obj );

		World.getInstance().addRoadSegment( obj );
	}

	/**
	 * Nyelő útelem létrehozása.
	 */
	public void create_drainRS( String key )
	{
		DrainRoadSegment obj = new DrainRoadSegment();

		storeObject( key, obj );

		World.getInstance().addRoadSegment( obj );
	}

	/**
	 * Forrás útelem létrehozása.
	 */
	public void create_sourceRS( String key )
	{
		SourceRoadSegment obj = new SourceRoadSegment();

		storeObject( key, obj );

		World.getInstance().addRoadSegment( obj );
	}

	/**
	 * PoliceStation útelem létrehozása.
	 */
	public void create_psRS( String key )
	{
		PoliceStation obj = new PoliceStation();

		storeObject( key, obj );

		World.getInstance().addRoadSegment( obj );
	}

	/**
	 * Bank létrehozása.
	 */
	public void create_bank( String key )
	{
		Bank obj = new Bank();

		storeObject( key, obj );

		World.getInstance().addRoadSegment( obj );
	}

	/**
	 * Rejtekhely útelem létrehozása.
	 */
	public void create_hideOut( String key )
	{
		HideOut obj = new HideOut();

		storeObject( key, obj );

		World.getInstance().addRoadSegment( obj );
	}

	/**
	 * Üres útelemet hoz létre.
	 */
	public void create_grass( String key )
	{
		Grass obj = new Grass();

		storeObject( key, obj );

		World.getInstance().addRoadSegment( obj );
	}

	/**
	 * Összeköti a paramérként kapott azonosítójú útelemeket olyan módon, hogy
	 * az id_from azonosítójú útelemből indul a kapcsolat(tehát onnan kimenő
	 * lesz, az id_to-nál bejövő).
	 */
	public void connect_rs( String key_in, String key_out, String id_from, String id_to )
	{
		RoadSegment from = (RoadSegment)_objects.get( id_from );
		RoadSegment to = (RoadSegment)_objects.get( id_to );

		RoadSegmentWrapper inRSW = to.addIncomingSegment( from ), outRSW = from.addOutgoingSegment( to );

		storeObject( key_in, inRSW );
		storeObject( key_out, outRSW );
	}

	/**
	 * A paraméterként megadott azonosítójú útelemen létrehoz egy Civil
	 * járművet.
	 */
	public void create_civilian( String key )
	{
		storeObject( key, new Civilian() );
	}

	/**
	 * A paraméterként megadott azonosítójú útelemen létrehoz egy Rendőr
	 * járművet.
	 */
	public void create_police( String key )
	{
		storeObject( key, new Police() );
	}

	/**
	 * A paraméterként megadott azonosítójú útelemen létrehoz egy Játékos által
	 * vezérelt járművet.
	 */
	public void create_player( String key )
	{
		Robber robber = new Robber();
		storeObject( key, robber );
		World.getInstance().setPlayer( robber );
	}

	/**
	 * Alias a create_player-hez (sok félreértés elkerülésére).
	 * 
	 * @param key
	 *            Az eltárolandó kulcs.
	 */
	public void create_robber( String key )
	{
		create_player( key );
	}

	/**
	 * A paraméterként megadott azonosítójú útelemcsomagolón létrehoz egy
	 * stoptábla szabályt.
	 */
	public void create_stopTable( String key, String rsw_key )
	{
		RoadSegmentWrapper rsw = (RoadSegmentWrapper)_objects.get( rsw_key );
		Rule r = new StopSign();

		storeObject( key, r );

		rsw.getRules().add( r );
	}

	/**
	 * A paraméterként megadott azonosítójú útelemcsomagolón létrehoz
	 * egyközlekedési lámpát.
	 */
	public void create_trafficLight( String key, String rsw_key )
	{
		RoadSegmentWrapper rsw = (RoadSegmentWrapper)_objects.get( rsw_key );
		Rule r = new TrafficLight();

		storeObject( key, r );

		World.getInstance().registerSteppable( r );
		rsw.getRules().add( r );
	}

	/**
	 * A paraméterként megadott azonosítójú útelemcsomagolón létrehoz egy
	 * „húsvétinyúl” szabályt.
	 */
	public void create_bunny( String key, String rsw_key )
	{
		RoadSegmentWrapper rsw = (RoadSegmentWrapper)_objects.get( rsw_key );
		Rule r = new Bunny();

		storeObject( key, r );

		rsw.getRules().add( r );
		World.getInstance().registerSteppable( r );
	}

	/**
	 * A világ „x” és „y” kiterjedését adja meg.
	 */
	public void set_worldDimensions( String x, String y ) throws NumberFormatException
	{
		int _x = Integer.parseInt( x );
		int _y = Integer.parseInt( y );

		World.getInstance().setDimension_x( _x );
		World.getInstance().setDimension_y( _y );
	}

	/*
	 * Vezérlés
	 */

	/**
	 * Kivált n darab órajel ütést.
	 */
	public void step( String n ) throws NumberFormatException
	{
		int max = Integer.parseInt( n );

		for ( int i = 0; i < max; ++i )
		{
			World.getInstance().step();
		}
	}

	/**
	 * Megváltoztatja a megadott jármű mozgási irányát az adott szög irányába(0◦
	 * -„3 óra”,90◦ -„12óra”,stb.)
	 * 
	 * @param key
	 *            A jármű kulcsa.
	 * @param angle
	 *            A szög (sztring-ben, float radiánként értelmezve)
	 */
	public void change_direction( String key, String angle ) throws NumberFormatException
	{
		float radian = (float)( ( Math.PI / 180.0 ) * Float.valueOf( angle ) );

		Vehicle v = (Vehicle)_objects.get( key );

		HumanStrategy hs = (HumanStrategy)v.getStrategy();
		hs.setChosenAngle( radian );
	}

	/**
	 * Egy járművet egy adott útelemre helyez.
	 * 
	 * @param key
	 *            A jármű kulcsa.
	 * @param rs_key
	 *            Az útelem kulcsa.
	 */
	public void enter( String key, String rs_key )
	{
		Vehicle v = (Vehicle)_objects.get( key );
		RoadSegment rs = (RoadSegment)_objects.get( rs_key );
		rs.enter( v.getWrapper() );
	}

	/**
	 * Átállítja a random döntéseket; ilyenkor a program megkérdezi a
	 * felhasználót.
	 */
	public void set_interactiveMode( String b ) throws Exception
	{
		boolean value = Boolean.parseBoolean( b );
		Random.setInteractive( value );
	}

	/**
	 * Kilép a programból
	 */
	public void exit()
	{
		System.exit( 0 );
	}

	/**
	 * Kiírja a paraméterül kapott azonosítójú objektum állapotát, vagy a
	 * paraméter elhagyása esetén kiír egy összegfoglalást a játék és a benne
	 * lévő objektumok állapotáról.
	 * 
	 * @param key
	 *            Az objektum kulcsa.
	 */
	public void display( String key )
	{
		Object obj = _objects.get( key );
		Renderer.getRenderer( obj ).render( obj );
	}

	/**
	 * Kiírja a paraméterül kapott objektum állapotát, vagy a paraméter
	 * elhagyása esetén kiír egy összegfoglalást a játék és a benne lévő
	 * objektumok állapotáról.
	 * 
	 * @param obj
	 *            Az objektum.
	 */
	public void display( Object obj )
	{
		Renderer.getRenderer( obj ).render( obj );
	}

	public void display()
	{
		Iterator<GameObject> itr = _objects.values().iterator();

		while ( itr.hasNext() )
		{
			Object obj = itr.next();
			Renderer.getRenderer( obj ).render( obj );
		}

		display_game();
	}

	/**
	 * Információk a játék állapotáról.
	 */
	public void display_game()
	{
		Renderer.getRenderer( Game.getInstance() ).render( Game.getInstance() );
	}

	/**
	 * A key azonosítójú trafficlight állapotát az ellenkezőre állítja. Ez
	 * automatikusan is megtörténne, ha nem hívjuk meg ezt a parancsot.
	 */
	public void toggle_trafficLight( String key )
	{
		TrafficLight trL = (TrafficLight)_objects.get( key );
		trL.toggle();
	}

	/**
	 * A key azonosítójú Bunny állapotát az ellenkezőre állítja. Ez
	 * automatikusan is megtörténne, ha nem hívjuk meg ezt a parancsot.
	 */
	public void toggle_bunny( String key )
	{
		Bunny b = (Bunny)_objects.get( key );
		b.toggle();
	}

	/**
	 * A key azonosítójú járművön beállítja az aktuális sebességet.
	 */
	public void set_vehicleSpeed( String key, String value ) throws NumberFormatException
	{
		Vehicle v = (Vehicle)_objects.get( key );
		int speed = Integer.parseInt( value );
		v.setSpeed( speed );
	}
}
